/******************************************************************************
This file is part of AppKit.
Project: appkit
Author : FergusZeng
Email  : cblock@126.com
git	   : https://gitee.com/newgolo/appkit.git
*******************************************************************************
MIT License

Copyright (c) 2022 cblock@126.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
******************************************************************************/
#include "appkit/system.h"

#include <ctype.h>
#include <errno.h>
#include <libgen.h>
#include <linux/reboot.h>
#include <math.h>
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/reboot.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>

#if USE_UUID
#include <uuid/uuid.h>
#endif

#include <string>

#include "appkit/datetime.h"
#include "appkit/fileio.h"
#include "appkit/strutil.h"
#include "appkit/tracer.h"

extern char** environ; /* 当前环境变量(Linux全局变量) */

namespace appkit {
using namespace std;  // NOLINT
#define READ 0
#define WRITE 1
static FILE* popen2(std::string command, std::string type, pid_t* pid) {
    pid_t childPid;
    int fd[2];
    pipe(fd);

    if ((childPid = fork()) == -1) {
        perror("fork");
        exit(1);
    }

    if (childPid == 0) {
        if (type == "r") {
            close(fd[READ]);
            dup2(fd[WRITE], 1);
        } else {
            close(fd[WRITE]);
            dup2(fd[READ], 0);
        }

        setpgid(childPid, childPid);
        execl("/bin/sh", "/bin/sh", "-c", command.data(), nullptr);
        exit(0);
    } else {
        if (type == "r") {
            close(fd[WRITE]);
        } else {
            close(fd[READ]);
        }
    }

    *pid = childPid;
    if (type == "r") {
        return fdopen(fd[READ], "r");
    }
    return fdopen(fd[WRITE], "w");
}

static int pclose2(FILE* fp, pid_t pid) {
    int stat;
    fclose(fp);
    while (waitpid(pid, &stat, 0) == -1) {
        if (errno != EINTR) {
            stat = -1;
            break;
        }
    }
    return stat;
}

int System::random(int max) {
    if (max <= 0) {
        max = RAND_MAX;
    }
    struct timeval current;
    unsigned int seed;
    gettimeofday(&current, nullptr);
    seed = current.tv_sec + current.tv_usec;
    srand((unsigned)seed);
    return rand() % max;
}

std::string System::uuid() {
#if USE_UUID
    uuid_t uuid;
    char buf[40] = {0};
    uuid_generate(uuid);
    uuid_unparse(uuid, buf);
    return std::string(buf);
#else
    return "";
#endif
}

int System::execute(const std::string& cmd) {
    pid_t status;
    status = system(cmd.data());
    if (-1 == status) {  // 创建子进程失败
        TRACE_ERR("system fork error!")
        return RC_ERROR;
    } else {
        if (WIFEXITED(status)) {             // cmd被成功调用
            if (0 == WEXITSTATUS(status)) {  // 判断cmd返回值
                return RC_OK;
            }
        }
    }
    TRACE_ERR("System::execute(\"%s\"), exit code: %d!", CSTR(cmd),
              WEXITSTATUS(status));
    return RC_ERROR;
}

int System::execute(const std::string& cmd, std::string* result,
                    int msTimeout) {
    pid_t pid;
    FILE* fp = popen2(CSTR(cmd), "r", &pid);
    if (!fp) {
        TRACE_ERR("System::execute(\"%s\"), popen failed!", CSTR(cmd));
        return RC_ERROR;
    }
    result->clear();
    int fd = fileno(fp);
    Time startTime = Time::fromMono();
    uint64 usTimeout = msTimeout * 1000;
    while (1) {
        if (msTimeout > 0 && Time::usSinceMono(startTime) >= usTimeout) {
            break;
        }
        fd_set readfds;
        FD_ZERO(&readfds);
        FD_SET(fd, &readfds);
        struct timeval tv;
        tv.tv_sec = 0;
        tv.tv_usec = 10000;
        int rc = select(fd + 1, &readfds, nullptr, nullptr, &tv);
        if (rc <= 0 || (!FD_ISSET(fd, &readfds))) {
            if (msTimeout == 0) {
                break;
            }
            continue;
        } else {
            char buf[256] = {0};
            rc = read(fd, buf, sizeof(buf));
            if (rc <= 0) {
                break;
            }
            result->append(std::string(buf, rc));
        }
    }
    kill(pid, SIGKILL);
    pclose2(fp, pid);
    return RC_OK;
}

void System::exit(int code, bool force) {
    sync();  // 同步所有IO
    if (force) {
        _exit(code);  // 不会清理IO缓存
    } else {
        exit(code);  // 会清理IO缓存
    }
}

int System::spawnOne(const std::string& procName, char** argv, int delays) {
    int pid = fork();
    if (pid < 0) {
        TRACE_ERR("System::spawnOne, fork error: %s!", ERRSTR);
        return RC_ERROR;
    } else if (pid == 0) {  // 子进程
        if (delays > 0) {
            sleep(delays);
        }
        if (execvpe(procName.data(), argv, environ) < 0) {
            PRINT_ERR("proc [%s] exec error!", procName.data());
            exit(errno);
        } else {
            exit(0);
        }
    }
    return pid;
}

void System::killOther(const std::string& procName) {
    std::vector<int> pids = getPidsByName(procName);
    auto self = getpid();
    for (auto i = 0; i < pids.size(); i++) {
        if (pids[i] == self) {
            continue;
        }
        if (kill(pids[i], SIGKILL) != 0) {
            TRACE_ERR("kill process[%s] %d error %d", procName.data(), pids[i],
                      errno);
        }
    }
}

void System::killGroup(const std::string& procName) {
    std::vector<int> pids = getPidsByName(procName);
    pid_t self = getpgrp();
    for (auto i = 0; i < pids.size(); i++) {
        pid_t pgid = getpgid(pids[i]);
        if (pgid == self) {
            continue;
        }
        if (killpg(pgid, SIGKILL) != 0) {
            TRACE_ERR("kill process[%d] groupid %d error %d", procName.data(),
                      pgid, errno);
        }
    }
}

std::vector<int> System::getPidsByName(const std::string& processName) {
    std::vector<int> pidList;
    if (processName.empty()) {
        return pidList;
    }
    auto fileNameList =
        Directory::getFileList("/proc", std::string("^[0-9]+$"));
    int num = fileNameList.size();
    for (auto i = 0; i < num; i++) {
        File file;
        std::string statusFile = "/proc/" + fileNameList[i] + "/status";
        if (!file.open(CSTR(statusFile), IO_MODE_RD_ONLY)) {
            continue;
        }
        std::string firstLine;
        if (RC_ERROR == file.readLine(&firstLine)) {
            continue;
        }
        std::vector<std::string> result = StrUtil::splitString(firstLine, ":");
        if (result.size() != 2) {
            continue;
        }
        std::string pid = StrUtil::trimTailBlank(result[1]);
        if (pid == processName) {
            pidList.push_back(std::stoi(fileNameList[i]));
        }
    }
    return pidList;
}

std::string System::getEnv(const std::string& name) {
    char* value = getenv(CSTR(name));
    if (value) {
        return std::string(value);
    }
    return "";
}

void System::setEnv(const std::string& name, const std::string& value) {
    setenv(CSTR(name), CSTR(value), 1);
}

bool System::setAffinity(std::vector<int> cpuIds) {
    // 使用命令查看进程xxx所运行的CPU: ps -eo pid,args,psr | grep xxx
    cpu_set_t set;
    CPU_ZERO(&set);
    for (auto& cpu : cpuIds) {
        CPU_SET(cpu, &set);
    }
    if (sched_setaffinity(0, sizeof(set), &set) < 0) {
        TRACE_ERR("System::setAffinity, Set CPU affinity error: %s", ERRSTR);
        return false;
    }
    return true;
}

int System::getProcMemory(const std::string& process) {
    auto pids = getPidsByName(process);
    if (pids.size() <= 0) {
        return -1;
    }
    File file;
    if (!file.open(StrUtil::format("/proc/%d/status", pids[0]),
                   IO_MODE_RD_ONLY)) {
        TRACE_ERR("System::getProcMemory, cannot read process status file!");
        return -1;
    }
    std::string lineStr;
    while (file.readLine(&lineStr) > 0) {
        if (lineStr.compare(0, 6, "VmRSS:") == 0) {
            auto sections = StrUtil::splitString(lineStr, " ");
            if (sections.size() > 1) {
                return std::stoi(sections[1]);
            }
        }
    }
    return -1;
}

bool System::reboot() {
    if (::reboot(LINUX_REBOOT_CMD_RESTART) < 0) {
        TRACE_WARN("system reboot failed!");
        return false;
    }
    TRACE_INFO("system reboot ...");
    return true;
}

bool System::setPreemptRT(int policy, int priority) {
    /* 锁定当前进程内存,防止被系统SWAP到硬盘中去,影响实时性能 */
    if (mlockall(MCL_CURRENT | MCL_FUTURE) != 0) {
        return false;
    }

    {
        /* 栈空间预留:Linux进程栈空间最大为8M
         * 故意让栈空间往下生长8K,其后的函数调用和局部变量将不再导致
         * 栈增长(依赖于page fault和内存申请)
         */
        unsigned char stack[8096];
        memset(stack, 0, sizeof(stack));
    }
    /* 设置当前进程调度策略和优先级 */
    pid_t pid = getpid();
    struct sched_param param;
    param.sched_priority = priority;
    if (sched_setscheduler(pid, policy, &param) < 0) {
        TRACE_ERR("main process(%d) cannot set policy:%d, priority:%d.", pid,
                  policy, priority);
        return false;
    }
    return true;
}

SysStat::SysStat() {}
SysStat::~SysStat() { m_thread.stop(); }

bool SysStat::start(int cpuId, uint32 msInterval,
                    std::function<void(void)> statCallback) {
    memset(m_lastCpuStat.name, 0, sizeof(m_lastCpuStat.name));
    memset(m_currCpuStat.name, 0, sizeof(m_currCpuStat.name));
    m_memTotalKB = 0;
    m_memFreeKB = 0;
    m_cpuId = cpuId;
    m_interval = msInterval;
    m_statCallback = statCallback;
    return m_thread.start(*this);
}
void SysStat::stop() { m_thread.stop(); }

float SysStat::getCpuUsage() {
    if (m_lastCpuStat.name[0] != 0 && m_currCpuStat.name[0] != 0) {
        float od = static_cast<float>(
            m_lastCpuStat.user + m_lastCpuStat.nice + m_lastCpuStat.system +
            m_lastCpuStat.idle + m_lastCpuStat.softirq + m_lastCpuStat.iowait +
            m_lastCpuStat.irq);
        float nd = static_cast<float>(
            m_currCpuStat.user + m_currCpuStat.nice + m_currCpuStat.system +
            m_currCpuStat.idle + m_currCpuStat.softirq + m_currCpuStat.iowait +
            m_currCpuStat.irq);
        if ((nd - od) != 0) {
            return (100.0 - (static_cast<float>(m_currCpuStat.idle -
                                                m_lastCpuStat.idle)) /
                                (nd - od) * 100.00);
        } else {
            return 0.0f;
        }
    }
    return 0.0f;
}

uint32 SysStat::getMemTotal() { return m_memTotalKB; }

uint32 SysStat::getMemFree() { return m_memFreeKB; }

void SysStat::run(const Thread& thread) {
    while (thread.isRunning()) {
        updateCpuStat();
        updateMemStat();
        Thread::msleep(m_interval);
        if (m_statCallback) {
            m_statCallback();
        }
    }
}

void SysStat::updateCpuStat() {
    File file;
    if (!file.open("/proc/stat", IO_MODE_RD_ONLY)) {
        TRACE_ERR_CLASS("cannot read cpu stat file !");
        return;
    }
    std::string statStr;
    while (file.readLine(&statStr) > 0) {
        auto sections = StrUtil::splitString(statStr, " ");
        if (sections.size() > 7 && sections[0].compare(0, 3, "cpu") == 0) {
            if (m_cpuId >= 0) {
                std::string cpuName = StrUtil::format("cpu%d", m_cpuId);
                if (sections[0].compare(0, cpuName.size(), cpuName) != 0) {
                    continue;
                }
            }
            m_lastCpuStat = m_currCpuStat;
            memcpy(m_currCpuStat.name, sections[0].data(), sections[0].size());
            m_currCpuStat.user = std::stoi(sections[1]);
            m_currCpuStat.nice = std::stoi(sections[2]);
            m_currCpuStat.system = std::stoi(sections[3]);
            m_currCpuStat.idle = std::stoi(sections[4]);
            m_currCpuStat.iowait = std::stoi(sections[5]);
            m_currCpuStat.irq = std::stoi(sections[6]);
            m_currCpuStat.softirq = std::stoi(sections[7]);
            break;
        }
    }
    file.close();
}

void SysStat::updateMemStat() {
    File file;
    if (!file.open("/proc/meminfo", IO_MODE_RD_ONLY)) {
        TRACE_ERR_CLASS("cannot read mem info file !");
        return;
    }
    std::string statStr;
    while (file.readLine(&statStr) > 0) {
        auto sections = StrUtil::splitString(statStr, " ");
        if (sections.size() > 2 &&
            sections[0].compare(0, 9, "MemTotal:") == 0) {
            m_memTotalKB = std::stoi(sections[1]);
        }
        if (sections.size() > 2 && sections[0].compare(0, 8, "MemFree:") == 0) {
            m_memFreeKB = std::stoi(sections[1]);
            break;
        }
    }
    file.close();
}

bool Endian::isBigEndian(void) {
    union EndianWrap_U {
        sint32 a;
        uint8 b;
    };
    union EndianWrap_U ew;
    ew.a = 0x01;
    return (ew.b) ? false : true;
}

uint16 Endian::host2LitEndian(uint16 value) {
    union EndianWrap_U {
        uint16 v;
        char c[2];
    };
    if (isBigEndian()) {
        union EndianWrap_U ew1, ew2;
        ew1.v = value;
        ew2.c[0] = ew1.c[1];
        ew2.c[1] = ew1.c[0];
        return ew2.v;
    }
    return value;
}

uint32 Endian::host2LitEndian(uint32 value) {
    union EndianWrap_U {
        uint32 v;
        char c[4];
    };
    if (isBigEndian()) {
        union EndianWrap_U ew1, ew2;
        ew1.v = value;
        ew2.c[0] = ew1.c[3];
        ew2.c[1] = ew1.c[2];
        ew2.c[2] = ew1.c[1];
        ew2.c[3] = ew1.c[0];
        return ew2.v;
    }
    return value;
}

float Endian::host2LitEndian(float value) {
    union EndianWrap_U {
        float v;
        char c[4];
    };
    if (isBigEndian()) {
        union EndianWrap_U ew1, ew2;
        ew1.v = value;
        ew2.c[0] = ew1.c[3];
        ew2.c[1] = ew1.c[2];
        ew2.c[2] = ew1.c[1];
        ew2.c[3] = ew1.c[0];
        return ew2.v;
    }
    return value;
}

uint16 Endian::host2BigEndian(uint16 value) {
    union EndianWrap_U {
        uint16 v;
        char c[2];
    };
    if (!isBigEndian()) {
        union EndianWrap_U ew1, ew2;
        ew1.v = value;
        ew2.c[0] = ew1.c[1];
        ew2.c[1] = ew1.c[0];
        return ew2.v;
    }
    return value;
}

uint32 Endian::host2BigEndian(uint32 value) {
    union EndianWrap_U {
        uint32 v;
        char c[4];
    };
    if (!isBigEndian()) {
        union EndianWrap_U ew1, ew2;
        ew1.v = value;
        ew2.c[0] = ew1.c[3];
        ew2.c[1] = ew1.c[2];
        ew2.c[2] = ew1.c[1];
        ew2.c[3] = ew1.c[0];
        return ew2.v;
    }
    return value;
}

float Endian::host2BigEndian(float value) {
    union EndianWrap_U {
        float v;
        char c[4];
    };
    if (!isBigEndian()) {
        union EndianWrap_U ew1, ew2;
        ew1.v = value;
        ew2.c[0] = ew1.c[3];
        ew2.c[1] = ew1.c[2];
        ew2.c[2] = ew1.c[1];
        ew2.c[3] = ew1.c[0];
        return ew2.v;
    }
    return value;
}

std::string Endian::unicodeOneToUtf8String(uint32 unicode) {
    int utflen = 0;
    char utf8code[7] = {0};

    unicode = host2LitEndian(unicode);

    if (unicode <= 0x0000007F) {
        // * U-00000000 - U-0000007F:  0xxxxxxx
        utf8code[0] = (unicode & 0x7F);
        utflen = 1;
    } else if (unicode >= 0x00000080 && unicode <= 0x000007FF) {
        // * U-00000080 - U-000007FF:  110xxxxx 10xxxxxx
        utf8code[1] = (unicode & 0x3F) | 0x80;
        utf8code[0] = ((unicode >> 6) & 0x1F) | 0xC0;
        utflen = 2;
    } else if (unicode >= 0x00000800 && unicode <= 0x0000FFFF) {
        // * U-00000800 - U-0000FFFF:  1110xxxx 10xxxxxx 10xxxxxx
        utf8code[2] = (unicode & 0x3F) | 0x80;
        utf8code[1] = ((unicode >> 6) & 0x3F) | 0x80;
        utf8code[0] = ((unicode >> 12) & 0x0F) | 0xE0;
        utflen = 3;
    } else if (unicode >= 0x00010000 && unicode <= 0x001FFFFF) {
        // * U-00010000 - U-001FFFFF:  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
        utf8code[3] = (unicode & 0x3F) | 0x80;
        utf8code[2] = ((unicode >> 6) & 0x3F) | 0x80;
        utf8code[1] = ((unicode >> 12) & 0x3F) | 0x80;
        utf8code[0] = ((unicode >> 18) & 0x07) | 0xF0;
        utflen = 4;
    } else if (unicode >= 0x00200000 && unicode <= 0x03FFFFFF) {
        // * U-00200000 - U-03FFFFFF:  111110xx 10xxxxxx 10xxxxxx 10xxxxxx
        // 10xxxxxx
        utf8code[4] = (unicode & 0x3F) | 0x80;
        utf8code[3] = ((unicode >> 6) & 0x3F) | 0x80;
        utf8code[2] = ((unicode >> 12) & 0x3F) | 0x80;
        utf8code[1] = ((unicode >> 18) & 0x3F) | 0x80;
        utf8code[0] = ((unicode >> 24) & 0x03) | 0xF8;
        utflen = 5;
    } else if (unicode >= 0x04000000 && unicode <= 0x7FFFFFFF) {
        // * U-04000000 - U-7FFFFFFF:  1111110x 10xxxxxx 10xxxxxx 10xxxxxx
        // 10xxxxxx 10xxxxxx
        utf8code[5] = (unicode & 0x3F) | 0x80;
        utf8code[4] = ((unicode >> 6) & 0x3F) | 0x80;
        utf8code[3] = ((unicode >> 12) & 0x3F) | 0x80;
        utf8code[2] = ((unicode >> 18) & 0x3F) | 0x80;
        utf8code[1] = ((unicode >> 24) & 0x3F) | 0x80;
        utf8code[0] = ((unicode >> 30) & 0x01) | 0xFC;
        utflen = 6;
    }
    std::string utf8str = utf8code;
    return utf8str;
}

uint32 Endian::utf8OneToUnicode(const char* utf8code) {
    // b1 表示UTF-8编码的高字节, b2 表示次高字节, ...
    uint8 b1, b2, b3, b4, b5, b6;

    uint8 utfbytes = 0;
    uint8 tmp = utf8code[0];
    while ((tmp & 0x80) != 0) {
        utfbytes++;
        tmp = tmp << 1;
    }

    uint32 unicode = 0x0;
    uint8* unibuf = reinterpret_cast<uint8*>(&unicode);
    switch (utfbytes) {
        case 0:
            unibuf[0] = utf8code[0];
            break;
        case 2:
            b1 = utf8code[0];
            b2 = utf8code[1];
            if ((b2 & 0xE0) != 0x80) {
                return 0;
            }
            unibuf[0] = (b1 << 6) + (b2 & 0x3F);
            unibuf[1] = (b1 >> 2) & 0x07;
            break;
        case 3:
            b1 = utf8code[0];
            b2 = utf8code[1];
            b3 = utf8code[2];
            if (((b2 & 0xC0) != 0x80) || ((b3 & 0xC0) != 0x80)) {
                return 0;
            }
            unibuf[0] = (b2 << 6) + (b3 & 0x3F);
            unibuf[1] = (b1 << 4) + ((b2 >> 2) & 0x0F);
            break;
        case 4:
            b1 = utf8code[0];
            b2 = utf8code[1];
            b3 = utf8code[2];
            b4 = utf8code[3];
            if (((b2 & 0xC0) != 0x80) || ((b3 & 0xC0) != 0x80) ||
                ((b4 & 0xC0) != 0x80)) {
                return 0;
            }
            unibuf[0] = (b3 << 6) + (b4 & 0x3F);
            unibuf[1] = (b2 << 4) + ((b3 >> 2) & 0x0F);
            unibuf[2] = ((b1 << 2) & 0x1C) + ((b2 >> 4) & 0x03);
            break;
        case 5:
            b1 = utf8code[0];
            b2 = utf8code[1];
            b3 = utf8code[2];
            b4 = utf8code[3];
            b5 = utf8code[4];
            if (((b2 & 0xC0) != 0x80) || ((b3 & 0xC0) != 0x80) ||
                ((b4 & 0xC0) != 0x80) || ((b5 & 0xC0) != 0x80)) {
                return 0;
            }
            unibuf[0] = (b4 << 6) + (b5 & 0x3F);
            unibuf[1] = (b3 << 4) + ((b4 >> 2) & 0x0F);
            unibuf[2] = (b2 << 2) + ((b3 >> 4) & 0x03);
            unibuf[3] = (b1 << 6);
            break;
        case 6:
            b1 = utf8code[0];
            b2 = utf8code[1];
            b3 = utf8code[2];
            b4 = utf8code[3];
            b5 = utf8code[4];
            b6 = utf8code[5];
            if (((b2 & 0xC0) != 0x80) || ((b3 & 0xC0) != 0x80) ||
                ((b4 & 0xC0) != 0x80) || ((b5 & 0xC0) != 0x80) ||
                ((b6 & 0xC0) != 0x80)) {
                return 0;
            }
            unibuf[0] = (b5 << 6) + (b6 & 0x3F);
            unibuf[1] = (b5 << 4) + ((b6 >> 2) & 0x0F);
            unibuf[2] = (b3 << 2) + ((b4 >> 4) & 0x03);
            unibuf[3] = ((b1 << 6) & 0x40) + (b2 & 0x3F);
            break;
        default:
            return 0;
            break;
    }
    return unicode;
}

uint32 Endian::bitsReverse(uint32 ref, uint8 bits) {
    uint32 value = 0;
    if (bits > 32) return value;
    for (uint8 i = 1; i < (bits + 1); i++) {
        if (ref & 0x01) {
            value |= 1 << (bits - i);
        }
        ref >>= 1;
    }
    return (ref << bits) + value;
}

FileWriteProtection::FileWriteProtection(const std::string& filePath) {
    if (File::exists(filePath)) {
        std::string result;
        std::string cmd("lsattr ");
        cmd.append(filePath);
        if (RC_OK != System::execute(cmd, &result) || result[4] != 'i') {
            return;
        }
        cmd = StrUtil::format("chattr -i %s", filePath.data());
        if (RC_OK == System::execute(cmd)) {
            m_filePath = filePath;
        } else {
            TRACE_ERR_CLASS("write protection disable failed!");
        }
    }
}

FileWriteProtection::~FileWriteProtection() {
    if (!m_filePath.empty() && File::exists(m_filePath)) {
        auto cmd = StrUtil::format("chattr +i %s", m_filePath.data());
        if (RC_OK == System::execute(cmd)) {
            return;
        } else {
            TRACE_ERR_CLASS("write protection enable failed!");
        }
    }
}

}  // namespace appkit
